#ifndef _SQUIRREL_VM_H_
#define _SQUIRREL_VM_H_

struct SquirrelError {
  SquirrelError();
  SquirrelError( const SQChar* s ): desc( s ) {}
  const SQChar *desc;
};

struct SquirrelVMSys {
  HSQUIRRELVM _VM;
  SquirrelObject * _root;
};


enum SquirrelInitFlags {
  sqifIO     = 0x01,
  sqifBlob   = 0x02,
  sqifMath   = 0x04,
  sqifString = 0x08,

  sqifAll    = 0xFF
};

class SquirrelVM {
    friend class SquirrelObject;
    friend struct SquirrelError;
  public:

    static void Init( SquirrelInitFlags flags = sqifAll );
    static BOOL_T IsInitialized() {return _VM == NULL ? FALSE : TRUE;}
    static void Shutdown();
    static void Cleanup();
    static BOOL_T Update();
    static SquirrelObject CompileScript( const SQChar *s );
    static SquirrelObject CompileBuffer( const SQChar *s, const SQChar * debugInfo = sqT( "console_buffer" ) );
    static SquirrelObject RunScript( const SquirrelObject &o, SquirrelObject *_this = NULL );
    static void PrintFunc( HSQUIRRELVM v, const SQChar* s, ... );
    static BOOL_T BeginCall( const SquirrelObject &func );
    static BOOL_T BeginCall( const SquirrelObject &func, SquirrelObject &_this );
    static void PushParam( const SquirrelObject &o );
    static void PushParam( const SQChar *s );
    static void PushParam( SQInteger n );
    static void PushParam( SQFloat f );
    static void PushParam( SQUserPointer up );
    static void PushParamNull();
    static SquirrelObject EndCall();
    static SquirrelObject CreateString( const SQChar *s );
    static SquirrelObject CreateTable();
    static SquirrelObject CreateArray( SQInteger size );
    static SquirrelObject CreateInstance( SquirrelObject &oclass );
    static SquirrelObject CreateFunction( SQFUNCTION func );
    static SquirrelObject CreateUserData( SQInteger size );

    static const SquirrelObject &GetRootTable();
    static HSQUIRRELVM GetVMPtr() { return _VM; }

    static void GetVMSys( SquirrelVMSys & vmSys ) {
      vmSys._VM   = _VM;
      vmSys._root = _root;
    }
    static void SetVMSys( const SquirrelVMSys & vmSys ) {
      _VM   = vmSys._VM;
      _root = vmSys._root;
    }
    static void PushValue( INT_T val ) {
      sq_pushinteger( _VM, val );
    }
    static void PushValue( FLOAT_T val ) {
      sq_pushfloat( _VM, val );
    }
    static void PushValue( bool val ) {
      sq_pushbool( _VM, val );
    }
    static void PushValue( SQChar * val ) {
      sq_pushstring( _VM, val, -1 );
    }
    static void PushValue( SQUserPointer val ) {
      sq_pushuserpointer( _VM, val );
    }
    static void PushValue( const SQChar * val ) {
      sq_pushstring( _VM, val, -1 );
    }
    static void PushObject( SquirrelObject & so ) {
      sq_pushobject( _VM, so._o );
    }
    static void Pop( SQInteger nelemstopop ) {
      sq_pop( _VM, nelemstopop );
    }
    static void PushRootTable( void );

    static SquirrelObject CreateFunction( SQFUNCTION func, const SQChar * scriptFuncName, const SQChar * typeMask = 0 );

    static SquirrelObject CreateFunction( SquirrelObject & so, SQFUNCTION func, const SQChar * scriptFuncName, const SQChar * typeMask = 0 );

    static SquirrelObject CreateFunctionGlobal( SQFUNCTION func, const SQChar * scriptFuncName, const SQChar * typeMask = 0 );

  private:
    static HSQUIRRELVM _VM;
    static SQInteger _CallState;
    static SquirrelObject * _root;
};

template<typename T>
inline BOOL_T SquirrelObject::ArrayAppend( T item ) {
  sq_pushobject( SquirrelVM::_VM, GetObjectHandle() );
  SquirrelVM::PushValue( item );
  BOOL_T res = sq_arrayappend( SquirrelVM::_VM, -2 ) == SQ_OK;
  sq_pop( SquirrelVM::_VM, 1 );
  return res;
}
#endif
